package it.cagiobox.fileutility.bo;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

enum Linetype {
	White, Comment, Section, Prop, Unknown
}

public class IniFile {
	private static Logger log = LogManager.getLogger(IniFile.class);

	private boolean _loaded;
	private String _fileName;
	private String _fullPath;
	private List<Section> _sections;
	private Integer _sectionIndex = -1;

	// reposition index
	public void moveFirstSection() {
		_sectionIndex = -1;
	}

	// get a section, return null if no more
	public Section getNextSection() {
		log.entry();

		Section section = null;

		_sectionIndex++;

		if (_sectionIndex <= _sections.size() - 1) {
			section = _sections.get(_sectionIndex);
		}

		return log.exit(section);
	}

	// get the list of sections
	public List<Section> getSections() {
		return _sections;
	}

	// default constructor
	// nothing of special to do because all variables are correctly initialized
	public IniFile() {
	}

	// add a section to the end of the list
	public void addSection(Section sect) {
		_sections.add(sect);
	}

	// IniFile has been successfully loaded from disk file
	public boolean isLoaded() {
		return _loaded;
	}

	// input file is parsed
	// sections and their properties are identified and appended to the object
	// model
	public boolean loadFromFile(String filePath) throws Exception {

		log.entry(filePath);

		File file;
		// try to get a file using provided file name, may be a relative path
		file = new File(filePath);

		if (!file.exists()) {
			String msg = "Il file " + filePath + " non esiste";
			log.error(msg);
			throw new Exception(msg);
		}

		// get the absolute path for log purpose, not used for the moment
		if (filePath.startsWith(".")) {
			// a relative path has been provided
			File parent = new File(file.getParent());
			_fullPath = parent.getCanonicalPath() + "/" + file.getName();

		} else {
			_fullPath = file.getCanonicalPath();
		}

		// get file name
		_fileName = file.getName();

		// create an empty list of sections
		_sections = new ArrayList<Section>();

		// reader of the disk file
		BufferedReader br = null;

		// counter of the corresponding read line file
		Integer lineCounter = 1;

		// the content of the read line will be stored here
		String sCurrentLine;

		// current processed section, valid thru many read lines
		Section currentSection = null;

		// a property, created and used if the specific line type is encountered
		Prop prop = null;

		try {

			// activated the buffered reader on the existing file
			br = new BufferedReader(new FileReader(_fullPath));

			while ((sCurrentLine = br.readLine()) != null) {

				// interpret the current line type and build corresponding
				// objects
				switch (findLineType(sCurrentLine)) {

				// empty line: ignore
				case White:
					break;

				// comment: ignore
				case Comment:
					break;

				// section: build object
				case Section:
					currentSection = new Section(sCurrentLine, lineCounter);
					addSection(currentSection);
					break;

				// property: build object
				case Prop:
					prop = new Prop(sCurrentLine, lineCounter, currentSection);
					currentSection.addProp(prop);
					break;

				// not known: ignore
				case Unknown:
					String msg1 = "LineType non supportato per la riga n. "
							+ lineCounter + " del file " + _fullPath + ":"
							+ sCurrentLine;
					log.error(msg1);
					break;

				// really, never reached
				default:
					String msg = "LineType non supportato per la riga n. "
							+ lineCounter + " del file " + _fullPath + ":"
							+ sCurrentLine;
					log.error(msg);
					throw new Exception(msg);
				}

				// update counter
				lineCounter++;
			}

		} catch (IOException e) {
			log.error(e.getMessage());
			e.printStackTrace();
		} finally {
			try {
				if (br != null)
					br.close();
			} catch (IOException ex) {
				log.error(ex.getMessage());
				ex.printStackTrace();
			}
		}

		_loaded = true;

		log.info("Caricate " + --lineCounter + " dal file " + filePath);
		return log.exit(true);
	}

	// line type categorizer
	public Linetype findLineType(String line) {
		log.entry();

		String sCurrentLine = line.trim();

		Linetype lineType;

		//Section
		if (sCurrentLine.startsWith("[") && sCurrentLine.endsWith("]")) {

			lineType = Linetype.Section;

		//Comment
		} else if (sCurrentLine.startsWith(";")) {

			lineType = Linetype.Comment;

		//property
		} else if (sCurrentLine.indexOf('=') > 0) {

			lineType = Linetype.Prop;

		//white
		} else if (sCurrentLine.isEmpty()) {

			lineType = Linetype.White;

		//unknown
		} else {

			lineType = Linetype.Unknown;

		}

		return log.exit(lineType);

	}

	// get file disk file name and extension
	public String getFileName() {
		return _fileName;
	}

	// get full path
	public String getFullPath() {
		return _fullPath;
	}

	// sort sections using the alphabetic comparator
	public void sort() {
		log.entry();

		// sort sections of the inifile in alphabetic order
		Collections
				.sort(_sections, new AlphabeticDescendingSectionComparator());

		// propagate sort request to each section
		for (Section sect : _sections) {
			sect.sort();
		}

		log.exit();
	}

}
